﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Text;

namespace FinMkts.DataProvider.Internal
{
    [Export]
    public class InternalPriceSerializer
    {
        public byte[] Serialize(InternalPrice price)
        {
            if (price == null) throw new ArgumentNullException("price");
            if (string.IsNullOrWhiteSpace(price.ListingCode)) throw new ArgumentException("ListingCode is not set");
            if (price.ListingCode.IndexOf(',') >= 0) throw new ArgumentException("ListingCode contains a comma");

            var priceFormat = "N" + 2;  //TODO: vary decimal places
            var csv = string.Join(",", new[]
                                           {
                                               price.ListingCode,
                                               price.Bid.HasValue ? price.Bid.Value.ToString(priceFormat) : "",
                                               price.Ask.HasValue ? price.Ask.Value.ToString(priceFormat) : "",
                                               price.LastTradePrice.HasValue ? price.LastTradePrice.Value.ToString(priceFormat) : "",
                                           });
            return Encoding.UTF8.GetBytes(csv);
        }

        public InternalPrice Deserialize(byte[] bytes)
        {
            if (bytes == null) throw new ArgumentNullException("bytes");
            string csv = Encoding.UTF8.GetString(bytes);
            var bits = csv.Split(',');

            var price = new InternalPrice();
            List<string> errors = null;
            List<string> extra = null;

            for (int i = 0; i < bits.Length; i++)
            {
                if (string.IsNullOrEmpty(bits[i])) continue;
                switch(i)
                {
                    case 0: price.ListingCode = bits[i]; break;
                    case 1:
                        double result;
                        if (double.TryParse(bits[i], out result))
                        {
                            price.Bid = result;
                        }
                        else
                        {
                            if (errors == null) errors = new List<string>();
                            errors.Add("Bid is invalid: " + bits[i]);
                        }
                        break;
                    case 2:
                        if (double.TryParse(bits[i], out result))
                        {
                            price.Ask = result;
                        }
                        else
                        {
                            if (errors == null) errors = new List<string>();
                            errors.Add("Ask is invalid: " + bits[i]);
                        }
                        break;
                    case 3:
                        if (double.TryParse(bits[i], out result))
                        {
                            price.LastTradePrice = result;
                        }
                        else
                        {
                            if (errors == null) errors = new List<string>();
                            errors.Add("LastTradePrice is invalid: " + bits[i]);
                        }
                        break;
                    default:
                        if (extra == null) extra = new List<string>();
                        extra.Add(bits[i]);
                        break;
                }
            }

            // record any deserialization errors
            if (errors != null)
            {
                price.Errors = errors;
            }
            
            // record any extra data
            if (extra != null)
            {
                price.ExtraData = extra;
            }

            return price;
        }
    }
}